﻿using UnityEngine;
using System.Collections;
using System.IO;
using System.Text;
using System;
using System.Collections.Generic;

#if UNITY_EDITOR
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Assembly-CSharp-Editor")]
#endif
abstract class NextChars : System.IDisposable
{

    static void CheckOrUTF8(ref Encoding encoding)
    {
        if (encoding == null) encoding = Encoding.UTF8;
    }

    public static NextChars FromString(string text, Encoding encoding = null)
    {
        CheckOrUTF8(ref encoding);
        return new NextCharsFromString(text, encoding);
    }

    public static NextChars FromStream(Stream stream, Encoding encoding = null)
    {
        CheckOrUTF8(ref encoding);
        return new NextCharsFromStream(stream, encoding);
    }

    public static NextChars FromFile(string path, Encoding encoding = null)
    {
        FileStream fstream = File.Open(path, FileMode.Open);
        return FromStream(fstream, encoding);
    }

    protected Encoding encoding { get; private set; }
    protected NextChars(Encoding encoding)
    {
        ChangeEncodingInt(encoding);
    }

    private void ChangeEncodingInt(Encoding encoding)
    {
        this.encoding = encoding;
    }

    public void ChangeEncoding(Encoding encoding)
    {
        var before = this.encoding;
        ChangeEncodingInt(encoding);
        OnEncodingChanged(before, encoding);
        ResetPosition();
    }



    /// <summary>
    /// Replace \r\n to \n, then replace \r to \n
    /// </summary>
    public abstract void PreprocessChars();
    public abstract void ResetPosition();
    public abstract bool CanGoNext { get; }
    public abstract char NextChar();

    /// <summary>
    /// Peek chars without changing position
    /// </summary>
    /// <param name="expect"></param>
    public string PeekFewChars(int expect = 7)
    {
        if (!CanGoNext) return "";
        if (expect < 1) return "";

        return PeekFew(expect);
    }
    protected abstract string PeekFew(int expect = 7);

    /// <summary>
    /// Encoding changing will auto reset position
    /// </summary>
    /// <param name="before"></param>
    /// <param name="current"></param>
    protected abstract void OnEncodingChanged(Encoding before, Encoding current);


    public virtual void Dispose()
    {
    }

    class NextCharsFromString : NextChars
    {
        string text;
        int next_read_index;

        public NextCharsFromString(string text, Encoding encoding) : base(encoding)
        {
            this.text = text;
        }

        public override void ResetPosition()
        {
            next_read_index = 0;
        }

        public override bool CanGoNext
        {
            get
            {
                return next_read_index < text.Length;
            }
        }

        public override char NextChar()
        {
            next_read_index++;
            return text[next_read_index - 1];
        }

        protected override void OnEncodingChanged(Encoding before, Encoding current)
        {
            text = current.GetString(Encoding.Convert(before, current, before.GetBytes(text)));
        }

        /// <summary>
        /// Replace \r\n to \n, then replace \r to \n
        /// </summary>
        public override void PreprocessChars()
        {
            text = text.Replace("\r\n", "\n").Replace("\r", "\n");
        }

        protected override string PeekFew(int expect = 7)
        {

            return text.Substring(next_read_index, Math.Min(expect, text.Length-next_read_index));
        }
    }

    class NextCharsFromStream : NextChars
    {
        Stream stream;
        Decoder decoder;


        byte[] byteBuffer;
        char[] charBuffer;

        const int byteBufferSize = 1024;
        int _maxCharsPerBuffer;

        int bytePos;//for preamble
        int charPos;

        int byteLen, charLen;

        /// <summary>
        /// 是否本次缓存已经把剩余流数据都读完了并且还有空余
        /// </summary>
        bool _isBlocked;

        bool streamCanGoNext;

        char nextChar;

        public NextCharsFromStream(Stream stream, Encoding encoding) : base(encoding)
        {
            this.stream = stream;
            byteBuffer = new byte[byteBufferSize];

            decoder = encoding.GetDecoder();

            SetCharBuffer();

            DealWithNext();
        }

        void SetCharBuffer()
        {
            _maxCharsPerBuffer = encoding.GetMaxCharCount(byteBufferSize);
            charBuffer = new char[_maxCharsPerBuffer];
        }

        int ReadToBuffer()
        {
            charPos = 0;
            charLen = 0;

            byteLen = stream.Read(byteBuffer, 0, byteBuffer.Length);
            if (byteLen == 0)
                return charLen;

            _isBlocked = byteLen < byteBuffer.Length;

            charLen += decoder.GetChars(byteBuffer, 0, byteLen, charBuffer, charLen);   //根据DecoderNLS，flush为false，也就是会保留上一次余留的未解析byte

            return charLen;
        }

        void DealWithNext()
        {
            if (charPos == charLen)
            {
                int num = ReadToBuffer();
                if (num == 0)
                {
                    streamCanGoNext = false;
                    return;
                }
            }

            streamCanGoNext = true;
            nextChar = charBuffer[charPos];
            charPos++;
        }

        public override bool CanGoNext
        {
            get
            {
                return streamCanGoNext || preChars.Count>0;
            }
        }

        public override char NextChar()
        {
            if (preChars.Count > 0)
                return preChars.Dequeue();

            return NextCharInternal();
        }

        char NextCharInternal()
        {
            var ret = nextChar;

            DealWithNext();

            if (shouldPreprocess)
            {
                if (ret == '\r')
                {
                    if (streamCanGoNext && nextChar == '\n')
                    {
                        DealWithNext();
                    }
                    ret = '\n';
                }
            }

            return ret;
        }

        public override void ResetPosition()
        {
            stream.Seek(0, SeekOrigin.Begin);
            bytePos = charPos = charLen = 0;
            decoder.Reset();
            preChars.Clear();
            DealWithNext();
        }

        protected override void OnEncodingChanged(Encoding before, Encoding current)
        {
            decoder = current.GetDecoder();
            SetCharBuffer();
        }

        public override void Dispose()
        {
            stream.Dispose();
        }

        bool shouldPreprocess = false;
        /// <summary>
        /// Replace \r\n to \n, then replace \r to \n
        /// </summary>
        public override void PreprocessChars()
        {
            shouldPreprocess = true;
        }

        /// <summary>
        /// 提前取出的chars
        /// </summary>
        Queue<char> preChars = new Queue<char>(16);
        protected override string PeekFew(int expect = 7)
        {
            if (expect == 1)
            {
                if (preChars.Count == 0) return nextChar.ToString();
                else return preChars.Peek().ToString();
            }

            while (preChars.Count < expect && streamCanGoNext)
            {
                preChars.Enqueue(NextCharInternal());
            }

            return new string(preChars.ToArray());
        }
    }

}